#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import json
import os
import subprocess
import argparse
from pathlib import Path
import logging
from typing import List, Dict, Any

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class DatabaseExporter:
    def __init__(self, config_path: str = 'config.json'):
        self.config_path = config_path
        self.config = self.load_config()
        
    def load_config(self) -> Dict[str, Any]:
        """加载配置文件"""
        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except FileNotFoundError:
            logger.error(f"配置文件 {self.config_path} 不存在")
            raise
        except json.JSONDecodeError:
            logger.error(f"配置文件 {self.config_path} JSON格式错误")
            raise
    
    def get_connection_params(self) -> Dict[str, Any]:
        """获取数据库连接参数"""
        db_config = self.config.get('database', {})
        return {
            'host': db_config.get('host', 'localhost'),
            'port': db_config.get('port', 3306),
            'user': db_config.get('user', 'root'),
            'password': db_config.get('password', ''),
            'ssl_disabled': db_config.get('ssl_disabled', True)
        }
    
    def get_all_databases(self) -> List[str]:
        """获取所有数据库列表（排除系统库）"""
        try:
            # 获取连接参数
            params = self.get_connection_params()
            
            # 使用环境变量传递密码
            env = os.environ.copy()
            env['MYSQL_PWD'] = params['password']
            
            # 构建命令
            ssl_option = "--ssl=0" if params['ssl_disabled'] else ""
            cmd = f"mysql -h{params['host']} -P{params['port']} -u{params['user']} {ssl_option} -e 'SHOW DATABASES;' -s"
            
            logger.debug(f"执行命令: {cmd}")
            
            result = subprocess.run(cmd, shell=True, capture_output=True, text=True, env=env)
            
            if result.returncode != 0:
                logger.error(f"获取数据库列表失败: {result.stderr}")
                return []
            
            databases = result.stdout.strip().split('\n')
            
            # 排除系统数据库
            system_dbs = ['information_schema', 'mysql', 'performance_schema', 'sys']
            return [db for db in databases if db and db not in system_dbs]
            
        except Exception as e:
            logger.error(f"获取数据库列表时发生错误: {e}")
            return []
    
    def export_database(self, database: str, output_dir: str) -> bool:
        """导出单个数据库"""
        try:
            # 创建输出目录
            os.makedirs(output_dir, exist_ok=True)
            
            # 构建输出文件路径
            output_file = os.path.join(output_dir, f"{database}.sql")
            
            # 获取连接参数
            params = self.get_connection_params()
            
            # 使用环境变量传递密码
            env = os.environ.copy()
            env['MYSQL_PWD'] = params['password']
            
            # 构建mysqldump命令（移除不支持的 --set-gtid-purged=OFF 参数）
            ssl_option = "--ssl=0" if params['ssl_disabled'] else ""
            cmd = f"mysqldump -h{params['host']} -P{params['port']} -u{params['user']} {ssl_option} --single-transaction --routines --triggers {database} > {output_file}"
            
            logger.info(f"正在导出数据库: {database} -> {output_file}")
            logger.debug(f"执行命令: {cmd}")
            
            # 执行导出命令
            result = subprocess.run(cmd, shell=True, capture_output=True, text=True, env=env)
            
            if result.returncode == 0:
                if os.path.exists(output_file):
                    file_size = os.path.getsize(output_file)
                    logger.info(f"成功导出数据库 {database}, 文件大小: {file_size / 1024 / 1024:.2f} MB")
                    return True
                else:
                    logger.warning(f"导出命令成功但未生成输出文件: {output_file}")
                    return False
            else:
                logger.error(f"导出数据库 {database} 失败: {result.stderr}")
                return False
                
        except Exception as e:
            logger.error(f"导出数据库 {database} 时发生错误: {e}")
            return False
    
    def test_connection(self) -> bool:
        """测试数据库连接"""
        try:
            # 获取连接参数
            params = self.get_connection_params()
            
            # 使用环境变量传递密码
            env = os.environ.copy()
            env['MYSQL_PWD'] = params['password']
            
            ssl_option = "--ssl=0" if params['ssl_disabled'] else ""
            cmd = f"mysql -h{params['host']} -P{params['port']} -u{params['user']} {ssl_option} -e 'SELECT 1;'"
            
            result = subprocess.run(cmd, shell=True, capture_output=True, text=True, env=env)
            
            if result.returncode == 0:
                logger.info("数据库连接测试成功")
                return True
            else:
                logger.error(f"数据库连接测试失败: {result.stderr}")
                return False
                
        except Exception as e:
            logger.error(f"数据库连接测试时发生错误: {e}")
            return False
    
    def check_mysqldump_version(self) -> bool:
        """检查 mysqldump 版本并返回是否支持 GTID 参数"""
        try:
            result = subprocess.run("mysqldump --version", shell=True, capture_output=True, text=True)
            if result.returncode == 0:
                version_output = result.stdout
                logger.info(f"mysqldump 版本信息: {version_output.strip()}")
                
                # 简单的版本检测逻辑
                if "MariaDB" in version_output:
                    logger.info("检测到 MariaDB，不支持 --set-gtid-purged 参数")
                    return False
                else:
                    # 对于 MySQL，检查版本号
                    import re
                    version_match = re.search(r'Ver\s+(\d+)\.(\d+)\.(\d+)', version_output)
                    if version_match:
                        major = int(version_match.group(1))
                        minor = int(version_match.group(2))
                        if major >= 5 and minor >= 6:
                            logger.info("检测到 MySQL 5.6+，支持 --set-gtid-purged 参数")
                            return True
                        else:
                            logger.info("检测到 MySQL 5.5 或更早版本，不支持 --set-gtid-purged 参数")
                            return False
                    return False
            return False
        except Exception as e:
            logger.warning(f"检查 mysqldump 版本时发生错误: {e}")
            return False
    
    def export_databases(self) -> bool:
        """导出数据库主函数"""
        try:
            # 测试数据库连接
            if not self.test_connection():
                logger.error("无法连接到数据库，请检查配置")
                return False
            
            # 检查 mysqldump 版本
            supports_gtid = self.check_mysqldump_version()
            
            # 获取输出目录
            output_dir = self.config.get('output_dir', './backup')
            logger.info(f"输出目录: {output_dir}")
            
            # 获取要导出的数据库列表
            specified_dbs = self.config.get('databases', [])
            
            if specified_dbs:
                # 导出指定的数据库
                databases_to_export = specified_dbs
                logger.info(f"导出指定的数据库: {', '.join(specified_dbs)}")
            else:
                # 导出所有数据库
                all_dbs = self.get_all_databases()
                databases_to_export = all_dbs
                if all_dbs:
                    logger.info(f"导出所有数据库: {', '.join(all_dbs)}")
                else:
                    logger.info("没有找到可导出的数据库")
                    return True
            
            # 导出每个数据库
            success_count = 0
            for db in databases_to_export:
                if self.export_database_with_gtid_check(db, output_dir, supports_gtid):
                    success_count += 1
            
            total_count = len(databases_to_export)
            logger.info(f"导出完成: 成功 {success_count}/{total_count} 个数据库")
            
            # 如果配置了指定数据库但全部失败，返回失败
            if specified_dbs and success_count == 0:
                return False
                
            return True
            
        except Exception as e:
            logger.error(f"导出过程中发生错误: {e}")
            return False
    
    def export_database_with_gtid_check(self, database: str, output_dir: str, supports_gtid: bool) -> bool:
        """根据 GTID 支持情况导出数据库"""
        try:
            # 创建输出目录
            os.makedirs(output_dir, exist_ok=True)
            
            # 构建输出文件路径
            output_file = os.path.join(output_dir, f"{database}.sql")
            
            # 获取连接参数
            params = self.get_connection_params()
            
            # 使用环境变量传递密码
            env = os.environ.copy()
            env['MYSQL_PWD'] = params['password']
            
            # 构建 mysqldump 命令
            ssl_option = "--ssl=0" if params['ssl_disabled'] else ""
            
            # 根据 GTID 支持情况构建命令
            if supports_gtid:
                cmd = f"mysqldump -h{params['host']} -P{params['port']} -u{params['user']} {ssl_option} --single-transaction --routines --triggers --set-gtid-purged=OFF {database} > {output_file}"
            else:
                cmd = f"mysqldump -h{params['host']} -P{params['port']} -u{params['user']} {ssl_option} --single-transaction --routines --triggers {database} > {output_file}"
            
            logger.info(f"正在导出数据库: {database} -> {output_file}")
            logger.debug(f"执行命令: {cmd}")
            
            # 执行导出命令
            result = subprocess.run(cmd, shell=True, capture_output=True, text=True, env=env)
            
            if result.returncode == 0:
                if os.path.exists(output_file):
                    file_size = os.path.getsize(output_file)
                    logger.info(f"成功导出数据库 {database}, 文件大小: {file_size / 1024 / 1024:.2f} MB")
                    return True
                else:
                    logger.warning(f"导出命令成功但未生成输出文件: {output_file}")
                    return False
            else:
                logger.error(f"导出数据库 {database} 失败: {result.stderr}")
                return False
                
        except Exception as e:
            logger.error(f"导出数据库 {database} 时发生错误: {e}")
            return False

def main():
    parser = argparse.ArgumentParser(description='MySQL数据库导出工具')
    parser.add_argument('--config', '-c', default='config.json', help='配置文件路径 (默认: config.json)')
    parser.add_argument('--debug', action='store_true', help='启用调试模式')
    parser.add_argument('--test-connection', action='store_true', help='测试数据库连接')
    
    args = parser.parse_args()
    
    if args.debug:
        logging.getLogger().setLevel(logging.DEBUG)
        logger.debug("调试模式已启用")
    
    try:
        exporter = DatabaseExporter(args.config)
        
        if args.test_connection:
            success = exporter.test_connection()
            if success:
                logger.info("数据库连接测试成功！")
            else:
                logger.error("数据库连接测试失败！")
                exit(1)
            return
        
        success = exporter.export_databases()
        
        if success:
            logger.info("所有数据库导出成功！")
        else:
            logger.error("部分或全部数据库导出失败！")
            exit(1)
            
    except Exception as e:
        logger.error(f"程序执行失败: {e}")
        exit(1)

if __name__ == "__main__":
    main()